package vworld;

import generated.business.admin.AdminServiceHelper;
import generated.business.avatar.AvatarServiceHelper;
import generated.business.avatar.AvatarServicePackage.Gender;
import generated.business.avatar.AvatarServicePackage.Mood;
import generated.business.avatar.AvatarServicePackage.Size;
import generated.business.connection.ConnectionServiceHelper;
import generated.business.message.MessageServiceHelper;
import generated.business.room.RoomServiceHelper;
import generated.business.user.UserServiceHelper;
import generated.business.world.WorldServiceHelper;
import generated.callback.ClientCallback;
import generated.domain.user.User;
import generated.domain.world.Matrix;
import generated.domain.world.World;

import java.io.IOException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Properties;

import org.omg.CORBA.ORB;
import org.omg.CosNaming.NameComponent;
import org.omg.CosNaming.NamingContextExt;
import org.omg.CosNaming.NamingContextExtHelper;
import org.omg.CosNaming.NamingContextPackage.CannotProceed;
import org.omg.CosNaming.NamingContextPackage.InvalidName;
import org.omg.CosNaming.NamingContextPackage.NotFound;
import org.omg.PortableServer.POA;
import org.omg.PortableServer.POAHelper;
import org.omg.PortableServer.POAManagerPackage.AdapterInactive;
import org.omg.PortableServer.POAPackage.ServantNotActive;
import org.omg.PortableServer.POAPackage.WrongPolicy;

import vworld.business.admin.AdminServiceServant;
import vworld.business.avatar.AvatarServiceServant;
import vworld.business.connection.ConnectionServiceServant;
import vworld.business.message.MessageServiceServant;
import vworld.business.room.RoomServiceServant;
import vworld.business.user.UserServiceServant;
import vworld.business.world.WorldServiceServant;
import vworld.dao.avatar.AvatarDAO;
import vworld.dao.message.MessageDAO;
import vworld.dao.room.RoomDAO;
import vworld.dao.user.UserDAO;
import vworld.dao.world.WorldDAO;

public class ServerSingleton {
	private static ServerSingleton INSTANCE = null;

	public Process namingService;
	public ORB orb;
	public POA rootPOA;
	public NamingContextExt namingcontextRef;

	public AdminServiceServant adminImpl = null;
	public AvatarServiceServant avatarImpl = null;
	public ConnectionServiceServant connectionImpl = null;
	public MessageServiceServant messageImpl = null;
	public RoomServiceServant roomImpl = null;
	public UserServiceServant userImpl = null;
	public WorldServiceServant worldImpl = null;

	public AvatarDAO AVATAR_DAO = new AvatarDAO();
	public MessageDAO MESSAGE_DAO = new MessageDAO();
	public RoomDAO ROOM_DAO = new RoomDAO();
	public UserDAO USER_DAO = new UserDAO();
	public WorldDAO WORLD_DAO = new WorldDAO();

	public List<World> WORLDS_LIST = new ArrayList<World>();
	public Map<String, ClientCallback> REGISTERED_CLIENT_MAP = new HashMap<String, ClientCallback>();

	private ServerSingleton() {
	}

	public synchronized static ServerSingleton getInstance() {
		if (INSTANCE == null)
			INSTANCE = new ServerSingleton();
		return INSTANCE;
	}

	public void main(String args[], String port) throws IOException,
			org.omg.CORBA.ORBPackage.InvalidName, AdapterInactive {
		// Launch of namingService
		System.out.println("Launch of namingService");
		namingService = Runtime.getRuntime().exec(
				"orbd -ORBInitialHost localhost -ORBInitialPort " + port); // -ORBInitialPort
		System.out.println("namingService launch successfully");

		Properties props = new Properties();
		props.put("org.omg.CORBA.ORBInitialPort", port);
		props.put("org.omg.CORBA.ORBInitialHost", "localhost");

		// create and initialize the ORB
		orb = ORB.init(args, props);
		System.out.println("Server : Initialized ORB...");

		// get reference to rootpoa & activate the POAManager
		rootPOA = POAHelper.narrow(orb.resolve_initial_references("RootPOA"));
		rootPOA.the_POAManager().activate();

		// get the root naming context ; NameService
		org.omg.CORBA.Object objRef = orb
				.resolve_initial_references("NameService");
		System.out.println("Server : Resolved NameService");
		namingcontextRef = NamingContextExtHelper.narrow(objRef);

		registerAdminServiceServant();
		registerAvatarServiceServant();
		registerConnectionServiceServant();
		registerMessageServiceServant();
		registerRoomServiceServant();
		registerUserServiceServant();
		registerWorldServiceServant();

		initAdminUser();

		WORLDS_LIST.add(worldImpl.createWorld("Midgard", new Matrix(2, 2)));
		WORLDS_LIST.add(worldImpl.createWorld("Geffen", new Matrix(4, 4)));
		WORLDS_LIST.add(worldImpl.createWorld("Payon", new Matrix(6, 2)));
		WORLDS_LIST.add(worldImpl.createWorld("Morroc", new Matrix(3, 8)));

		userImpl.createUser("Alexandre Chhua", "simple user",
				"alex@andre.chhua", "achhua", "achhua");
		userImpl.createUser("Stephane Favier", "simple user",
				"steph@ne.favier", "sfavier", "sfavier");
		userImpl.createUser("Nathalie Liesse", "simple user",
				"nath@lie.liesse", "nliesse", "nliesse");

		// start readthread and wait for incoming requests
		System.out.println("Server ready and waiting ...");

		// wait for invocations from clients
		// orb.run();
		//
	}

	private void initAdminUser() {
		User admin = userImpl.createUser("Administrator", "administrator",
				"administrator@mail.com", "admin", "admin");
		avatarImpl.createAvatar(admin, "Administrator", Mood
				.from_int(Mood._happy), Gender.from_int(Gender._male), Size
				.from_int(Size._giant));
	}

	private void registerAdminServiceServant() {
		// create admin service servant and register it with the ORB
		adminImpl = new AdminServiceServant();
		adminImpl.setOrb(orb);

		try {
			String adminServiceName = "AdminService";
			// Resolve the object reference in naming
			NameComponent[] path = namingcontextRef.to_name(adminServiceName);
			// bind the Object Reference in NameService
			namingcontextRef.rebind(path, AdminServiceHelper.narrow(rootPOA
					.servant_to_reference(adminImpl)));
		} catch (ServantNotActive e) {
			e.printStackTrace();
		} catch (WrongPolicy e) {
			e.printStackTrace();
		} catch (InvalidName e) {
			e.printStackTrace();
		} catch (NotFound e) {
			e.printStackTrace();
		} catch (CannotProceed e) {
			e.printStackTrace();
		}
	}

	private void registerAvatarServiceServant() {
		// create avatar service servant and register it with the ORB
		avatarImpl = new AvatarServiceServant();
		avatarImpl.setOrb(orb);

		try {
			String avatarServiceName = "AvatarService";
			// Resolve the object reference in naming
			NameComponent[] path = namingcontextRef.to_name(avatarServiceName);
			// bind the Object Reference in NameService
			namingcontextRef.rebind(path, AvatarServiceHelper.narrow(rootPOA
					.servant_to_reference(avatarImpl)));
		} catch (ServantNotActive e) {
			e.printStackTrace();
		} catch (WrongPolicy e) {
			e.printStackTrace();
		} catch (InvalidName e) {
			e.printStackTrace();
		} catch (NotFound e) {
			e.printStackTrace();
		} catch (CannotProceed e) {
			e.printStackTrace();
		}
	}

	private void registerConnectionServiceServant() {
		// create connection service servant and register it with the ORB
		connectionImpl = new ConnectionServiceServant();
		connectionImpl.setOrb(orb);

		try {
			String connectionServiceName = "ConnectionService";
			// Resolve the object reference in naming
			NameComponent[] path = namingcontextRef
					.to_name(connectionServiceName);
			// bind the Object Reference in NameService
			namingcontextRef.rebind(path, ConnectionServiceHelper
					.narrow(rootPOA.servant_to_reference(connectionImpl)));
		} catch (ServantNotActive e) {
			e.printStackTrace();
		} catch (WrongPolicy e) {
			e.printStackTrace();
		} catch (InvalidName e) {
			e.printStackTrace();
		} catch (NotFound e) {
			e.printStackTrace();
		} catch (CannotProceed e) {
			e.printStackTrace();
		}
	}

	private void registerMessageServiceServant() {
		// create message service servant and register it with the ORB
		messageImpl = new MessageServiceServant();
		messageImpl.setOrb(orb);

		try {
			String messageServiceName = "MessageService";
			// Resolve the object reference in naming
			NameComponent[] path = namingcontextRef.to_name(messageServiceName);
			// bind the Object Reference in NameService
			namingcontextRef.rebind(path, MessageServiceHelper.narrow(rootPOA
					.servant_to_reference(messageImpl)));
		} catch (ServantNotActive e) {
			e.printStackTrace();
		} catch (WrongPolicy e) {
			e.printStackTrace();
		} catch (InvalidName e) {
			e.printStackTrace();
		} catch (NotFound e) {
			e.printStackTrace();
		} catch (CannotProceed e) {
			e.printStackTrace();
		}
	}

	private void registerRoomServiceServant() {
		// create room service servant and register it with the ORB
		roomImpl = new RoomServiceServant();
		roomImpl.setOrb(orb);

		try {
			String roomServiceName = "RoomService";
			// Resolve the object reference in naming
			NameComponent[] path = namingcontextRef.to_name(roomServiceName);
			// bind the Object Reference in NameService
			namingcontextRef.rebind(path, RoomServiceHelper.narrow(rootPOA
					.servant_to_reference(roomImpl)));
		} catch (ServantNotActive e) {
			e.printStackTrace();
		} catch (WrongPolicy e) {
			e.printStackTrace();
		} catch (InvalidName e) {
			e.printStackTrace();
		} catch (NotFound e) {
			e.printStackTrace();
		} catch (CannotProceed e) {
			e.printStackTrace();
		}
	}

	private void registerUserServiceServant() {
		// create user service servant and register it with the ORB
		userImpl = new UserServiceServant();

		try {
			String userServiceName = "UserService";
			// Resolve the object reference in naming
			NameComponent[] path = namingcontextRef.to_name(userServiceName);
			// bind the Object Reference in NameService
			namingcontextRef.rebind(path, UserServiceHelper.narrow(rootPOA
					.servant_to_reference(userImpl)));
		} catch (ServantNotActive e) {
			e.printStackTrace();
		} catch (WrongPolicy e) {
			e.printStackTrace();
		} catch (InvalidName e) {
			e.printStackTrace();
		} catch (NotFound e) {
			e.printStackTrace();
		} catch (CannotProceed e) {
			e.printStackTrace();
		}
	}

	private void registerWorldServiceServant() {
		// create world service servant and register it with the ORB
		worldImpl = new WorldServiceServant();

		try {
			String worldServiceName = "WorldService";
			// Resolve the object reference in naming
			NameComponent[] path = namingcontextRef.to_name(worldServiceName);
			// bind the Object Reference in NameService
			namingcontextRef.rebind(path, WorldServiceHelper.narrow(rootPOA
					.servant_to_reference(worldImpl)));
		} catch (ServantNotActive e) {
			e.printStackTrace();
		} catch (WrongPolicy e) {
			e.printStackTrace();
		} catch (InvalidName e) {
			e.printStackTrace();
		} catch (NotFound e) {
			e.printStackTrace();
		} catch (CannotProceed e) {
			e.printStackTrace();
		}
	}

	public List<ClientCallback> getAdminCallBackList() {
		List<ClientCallback> adminCallbacks = new ArrayList<ClientCallback>();
		for (Map.Entry<String, ClientCallback> e : REGISTERED_CLIENT_MAP
				.entrySet()) {
			if (e.getValue().userRole().equals("administrator")) {
				adminCallbacks.add(e.getValue());
			}
		}

		return adminCallbacks;
	}
}
